<?php
// +----------------------------------------------------------------------
// | XShop小程序商城
// +----------------------------------------------------------------------
// | Author: Peak Xin <xinyflove@gmail.com>
// +----------------------------------------------------------------------

namespace app\api\service;

use app\lib\enum\ScopeEnum;
use app\lib\exception\TokenException;
use app\lib\exception\WeChatException;
use think\Exception;
use app\api\model\User as UserModel;

class UserToken extends Token {
    
    protected $_code;
    protected $_wxAppID;
    protected $_wxAppSecret;
    protected $_wxLoginUrl;

    public function __construct($code)
    {
        $this->_code = $code;
        $this->_wxAppID = config('wx.app_id');
        $this->_wxAppSecret = config('wx.app_secret');
        $this->_wxLoginUrl = sprintf(config('wx.login_url')
            , $this->_wxAppID
            , $this->_wxAppSecret
            , $this->_code
            );
    }

    /**
     * 获取Token
     * @return string
     * @throws Exception
     * @throws WeChatException
     */
    public function get()
    {
        $result = curl_get($this->_wxLoginUrl);
        $wxResult = json_decode($result, true);
        if(empty($wxResult))
        {
            throw new Exception('获取session_key及openID异常，微信内部错误');
        }
        $loginFail = array_key_exists('errcode', $wxResult);
        if($loginFail)
        {
            $this->__processLoginError($wxResult);
        }
        return $this->__grantToken($wxResult);
    }

    /**
     *
     * 微信登录过程错误
     * @param $wxResult
     * @throws WeChatException
     */
    private function __processLoginError($wxResult)
    {
        throw new WeChatException([
            'msg' => $wxResult['errmsg'],
            'errorCode' => $wxResult['errcode'],
        ]);
    }

    /**
     * 授予Token
     * @param $wxResult
     * @return string
     * @throws TokenException
     */
    private function __grantToken($wxResult)
    {
        // 拿到openid
        $openid = $wxResult['openid'];
        // 数据库里查看，这个openid是否已存在
        $user = UserModel::getUserByOpenID($openid);
        // 如果存在则不处理，如果不存在则新增一条user记录
        if($user)
        {
            $uid = $user->id;
        }
        else
        {
            $uid = $this->__newUser($openid);
        }
        // 生成令牌，准备缓存数据，写入缓存
        $cacheValue = $this->__prepareCacheValue($wxResult, $uid);
        // 把令牌返回到客户端
        $token = $this->__saveToCache($cacheValue);

        return $token;
    }

    /**
     * 生成新用户
     * @param $openid
     * @return mixed
     */
    private function __newUser($openid)
    {
        $user = UserModel::create([
            'openid' => $openid,
        ]);
        return $user->id;
    }

    /**
     * 准备缓存值
     * @param $wxResult
     * @param $uid
     * @return mixed
     */
    private function __prepareCacheValue($wxResult, $uid)
    {
        $cacheValue = $wxResult;
        $cacheValue['uid'] = $uid;
        $cacheValue['scope'] = ScopeEnum::User;
        //$cacheValue['scope'] = 15;
        return $cacheValue;
    }

    /**
     * 保存到缓存
     * @param $cacheValue
     * @return string
     * @throws TokenException
     */
    private function __saveToCache($cacheValue)
    {
        $key = self::generateToken();
        $value = json_encode($cacheValue);
        $expire_in = config('setting.token_expire_in');

        $result = cache($key, $value, $expire_in);
        if(!$result)
        {
            throw new TokenException([
                'msg' => '服务器缓存异常',
                'errorCode' => 10005
            ]);
        }

        return $key;
    }
}